#include <iostream>
#include <iomanip>

#include "Matrix.h"

using namespace std;

Matrix::Matrix() :Mat_he(0), Mat_w(0), Mat_da(NULL)
{
}
Matrix::Matrix(int h, int w)
{
	Mat_he = h;
	Mat_w = w;
	Mat_da = new double* [Mat_he];


	for (int i = 0; i < Mat_he; i++)
	{
		Mat_da[i] = new double[Mat_w];
		for (int j = 0; j < Mat_w; j++)
		{
			Mat_da[i][j] = 0;

		}
	}
}
Matrix::Matrix(int h, int w, double val)
{
	Mat_he = h;
	Mat_w = w;
	Mat_da = new double* [Mat_he];

	for (int i = 0; i < Mat_he; i++)
	{
		Mat_da[i] = new double[Mat_w];
		for (int j = 0; j < Mat_w; j++)
		{
			Mat_da[i][j] = val;
		}
	}
}
Matrix::Matrix(const Matrix& m)
{
	Mat_w = m.Mat_w;
	Mat_he = m.Mat_he;
	Mat_da = new double* [Mat_he];

	for (int i = 0; i < Mat_he; i++)
	{
		Mat_da[i] = new double[Mat_w];
		for (int j = 0; j < Mat_w; j++)
		{
			Mat_da[i][j] = m.Mat_da[i][j];
		}
	}
}
Matrix::~Matrix()
{
	if (Mat_da != NULL)
	{
		for (int i = 0; i < Mat_he; i++)
		{
			if (Mat_da[i] != NULL)
			{
				delete[] Mat_da[i];
				Mat_da[i] = NULL;
			}
		}
		delete[] Mat_da;
		Mat_da = NULL;
	}
}


void Matrix::ReadText(const char* filename)
{
	FILE* fp = NULL;
	fopen_s(&fp, filename, "rb");

	fread(&Mat_he, sizeof(Mat_he), 1, fp);
	fread(&Mat_w, sizeof(Mat_w), 1, fp);

	Mat_da = new double* [Mat_he];

	for (int i = 0; i < Mat_he; i++)
	{
		Mat_da[i] = new double[Mat_w * 3];
		for (int j = 0; j < Mat_w; j++)
		{
			unsigned char temp[3] = { 0 };
			for (int t = 0; t < 3; t++)
			{
				fread(&temp[t], 1, 1, fp);
			}
			Mat_da[i][j * 3 + 0] = (temp[0] + temp[1] + temp[2]) / 3;
			Mat_da[i][j * 3 + 1] = (temp[0] + temp[1] + temp[2]) / 3;
			Mat_da[i][j * 3 + 2] = (temp[0] + temp[1] + temp[2]) / 3;
		}
	}

	fclose(fp);
}
void Matrix::WriteText(const char* filename)
{
	
	FILE* fp = NULL;
	fopen_s(&fp, filename, "wb");

	fwrite(&Mat_he, sizeof(Mat_he), 1, fp);
	fwrite(&Mat_w, sizeof(Mat_w), 1, fp);

	for (int i = 0; i < Mat_he; i++)
	{
		for (int j = 0; j < Mat_w; j++)
		{
			fwrite((unsigned char*)&Mat_da[i][j], sizeof(unsigned char), 1, fp);
		}
	}

	fclose(fp);
}

void Matrix::str(int h, int w)
{
	Mat_da = new double* [h];

	for (int i = 0; i < h; i++)
	{
		Mat_da[i] = new double[w];
		for (int j = 0; j < w; j++)
		{
			Mat_da[i][j] = 0;
		}
	}
}
void Matrix::Zeros(int h, int w)
{
	Mat_he = h;
	Mat_w = w;
	str(h, w);

}
void Matrix::Ones(int h, int w)
{
	Mat_he = h;
	Mat_w = w;
	str(h, w);
	for (int i = 0; i < Mat_he; i++)
	{

		for (int j = 0; j < Mat_w; j++)
		{
			Mat_da[i][j] = 1;
		}
	}

}
void Matrix::Random(int h, int w)
{
	Mat_he = h;
	Mat_w = w;
	str(h, w);
	for (int i = 0; i < Mat_he; i++)
	{
		for (int j = 0; j < Mat_w; j++)
		{
			Mat_da[i][j] = (double)rand() / RAND_MAX;
		}
	}
}
void Matrix::Random(int h, int w, int)
{
	Mat_he = h;
	Mat_w = w;
	str(h, w);
	for (int i = 0; i < Mat_he; i++)
	{
		for (int j = 0; j < Mat_w; j++)
		{
			Mat_da[i][j] = (double)rand() * 10;
		}
	}
}
void Matrix::Identity(int n) 
{
	Zeros(n, n);
}


Matrix Matrix::MajorDiagonal()
{
	Matrix m(1, Mat_he);
	for (int i = 0; i < Mat_he; i++)
	{
		cout << Mat_da[i][i] << " ";
		m.Mat_da[0][i] = Mat_da[i][i];
	}
	return m;
	cout << endl;
}
Matrix Matrix::MinorDiagonal()
{
	Matrix m(1, Mat_he);
	for (int i = 0; i < Mat_he; i++)
	{
		cout <<Mat_da[i][Mat_w - 1 - i] << " ";
		m.Mat_da[0][i] = Mat_da[i][Mat_w - 1 - i];
	}
	cout << endl;
	return m;
}
Matrix Matrix::Row(int n)
{
	Matrix m(1, Mat_w);
	for (int i = 0; i < Mat_w; i++)
	{
		m.Mat_da[0][i] = Mat_da[n - 1][i];
	}
	return m;
}
Matrix Matrix::Column(int n)
{
	Matrix m(1, Mat_he);
	for (int i = 0; i < Mat_he; i++)
	{
		m.Mat_da[0][i] = Mat_da[i][n - 1];
	}
	return m;
}


void Matrix::Transpose()
{

	double** data = new double* [Mat_w];
	for (int i = 0; i < Mat_w; i++)
	{
		data[i] = new double[Mat_he];
		for (int j = 0; j < Mat_he; j++)
		{
			data[i][j] = Mat_da[j][i];
		}
	}
	if (Mat_da != NULL)
	{
		for (int i = 0; i < Mat_he; i++)
		{
			if (Mat_da[i] != NULL)
			{
				delete[] Mat_da[i];
				Mat_da[i] = NULL;
			}
		}
		delete[] Mat_da;
		Mat_da = NULL;
	}

	Mat_da = data;
	int h = Mat_he;
	Mat_he = Mat_w;
	Mat_w = h;
}


double& Matrix::At(int row, int col)
{
	return Mat_da[row - 1][col - 1];
}
void Matrix::Set(int row, int col, double value) 
{
	Mat_da[row - 1][col - 1] = value;
}
void Matrix::Set(double value)
{
	for (int i = 0; i < Mat_he; i++)
	{
		for (int j = 0; j < Mat_w; j++)
		{
			Mat_da[i][j] = value;
		}
	}
}


void Matrix::Normalize()
{
	int i1 = 0, j1 = 0;
	int i2 = 0, j2 = 0;
	for (int i = 0; i < Mat_he; i++)
	{
		for (int j = 0; j < Mat_w; j++)
		{
			if (Mat_da[i][j] > Mat_da[i1][j1])
			{
				i1 = i;
				j1 = j;
			}
			if (Mat_da[i][j] < Mat_da[i2][j2])
			{
				i2 = i;
				j2 = j;
			}
		}
	}
	double max = Mat_da[i1][j1];
	double min = Mat_da[i2][j2];

	for (int i = 0; i < Mat_he; i++)
	{
		for (int j = 0; j < Mat_w; j++)
		{
			if ((i == i1 && j == j1) || (i ==i2 && j == j2))
			{
				continue;
			}
			Mat_da[i][j] = (Mat_da[i][j] - min) / max;
		}
	}
	Mat_da[i1][j1] = 1;
	Mat_da[i2][j2] = 0;
}


void Matrix::Reshape(int h, int w)
{
	if (h * w == Mat_he * Mat_w)
	{
		double** data = new double* [h];
		for (int i = 0; i < h; i++)
		{
			data[i] = new double[w];
		}

		int m_i = 0, m_j = 0;
		for (int i = 0; i < h; i++)
		{
			for (int j = 0; j < w; j++)
			{
				if (m_j == Mat_w)
				{
					m_j = 0;
					m_i++;
				}
				data[i][j] = Mat_da[m_i][m_j];
				m_j++;
			}
		}

		if (Mat_da != NULL)
		{
			for (int i = 0; i < Mat_he; i++)
			{
				if (Mat_da[i] != NULL)
				{
					delete[] Mat_da[i];
					Mat_da[i] = NULL;
				}
			}
			delete[] Mat_da;
			Mat_da = NULL;
		}
		Mat_he = h;
		Mat_w = w;
		Mat_da = data;
	}
	else
	{
		cout << "error" << endl;
	}
}
bool Matrix::IsEmpty()
{
	
	if (Mat_da)
	{
		for (int i = 0; i < Mat_he; i++)
		{
			for (int j = 0; j < Mat_w; j++)
			{
				if (Mat_da[i][j] != 0)
				{
					return false;
				}
			}
		}
	}
	return true;
}
bool Matrix::IsSquare()
{
	if (Mat_w == Mat_he)
	{
		return true;
	}
	else
		return false;
}
void Matrix::CopyTo(Matrix& m)
{
	m.Mat_he = Mat_he;
	m.Mat_w = Mat_w;

	m.Mat_da = new double* [Mat_he];
	for (int i = 0; i < Mat_he; i++)
	{
		m.Mat_da[i] = new double[Mat_w];
		for (int j = 0; j < Mat_w; j++)
		{
			m.Mat_da[i][j] = Mat_da[i][j];
		}
	}
}
void Matrix::Mult(double s) 
{
	for (int i = 0; i < Mat_he; i++)
	{
		for (int j = 0; j < Mat_w; j++)
		{
			Mat_da[i][j] *= s;
		}
	}
}
void Matrix::Cat(Matrix& m, int code)
{
	if (code == 1)
	{
		int height = Mat_he;
		int width = Mat_w + m.Mat_w;
		double** data = new double* [height];
		for (int i = 0; i < height; i++)
		{
			data[i] = new double[width];
			for (int j = 0; j < width; j++)
			{
				if (j < Mat_w)
				{
					data[i][j] = Mat_da[i][j];
				}
				else
				{
					data[i][j] = m.Mat_da[i][j - Mat_w];
				}
			}
		}

		if (Mat_da != NULL)
		{
			for (int i = 0; i < Mat_he; i++)
			{
				if (Mat_da[i] != NULL)
				{
					delete[] Mat_da[i];
					Mat_da[i] = NULL;
				}
			}
			delete[] Mat_da;
			Mat_da = NULL;
		}
		Mat_he = height;
		Mat_w = width;
		Mat_da = data;
	}
	else if (code == 2)
	{
		int height = Mat_he + m.Mat_he;
		
		int width = Mat_w;
		double** data = new double* [height];
		for (int i = 0; i < height; i++)
		{
			data[i] = new double[width];
			for (int j = 0; j < width; j++)
			{
				if (i < Mat_he)
				{
					data[i][j] = Mat_da[i][j];
				}
				else
				{
					data[i][j] = m.Mat_da[i - Mat_he][j];
				}
			}
		}

		if (Mat_da != NULL)
		{
			for (int i = 0; i < Mat_he; i++)
			{
				if (Mat_da[i] != NULL)
				{
					delete[] Mat_da[i];
					Mat_da[i] = NULL;
				}
			}
			delete[] Mat_da;
			Mat_da = NULL;
		}
		Mat_he = height;
		Mat_w = width;
		Mat_da = data;
	}
	else
	{
		cout << "error" << endl;
	}
}


Matrix Add(const Matrix& m1, const Matrix& m2)
{
	Matrix m(m1);
	for (int i = 0; i < m.Mat_he; i++)
	{
		for (int j = 0; j < m.Mat_w; j++)
		{
			m.Mat_da[i][j] += m2.Mat_da[i][j];
		}
	}

	for (int i = 0; i < m.Mat_he; i++)
	{
		for (int j = 0; j < m.Mat_w; j++)
		{
			if (m.Mat_da[i][j] > 255)
			{
				m.Mat_da[i][j] = 255;
			}
		}
	}
	return m;
}
Matrix Sub(const Matrix& m1, const Matrix& m2) 
{
	Matrix m(m1);
	for (int i = 0; i < m.Mat_he; i++)
	{
		for (int j = 0; j < m.Mat_w; j++)
		{
			m.Mat_da[i][j] -= m2.Mat_da[i][j];
		}
	}

	for (int i = 0; i < m.Mat_he; i++)
	{
		for (int j = 0; j < m.Mat_w; j++)
		{
			if (m.Mat_da[i][j] > 255)
			{
				m.Mat_da[i][j] = 255;
			}
			else if (m.Mat_da[i][j] < 0)
			{
				m.Mat_da[i][j] = 0;
			}
		}
	}
	return m;
}
void Swap(Matrix& a, Matrix& b) 
{
	int temp_he = a.Mat_he;
	a.Mat_he = b.Mat_he;
	b.Mat_he = temp_he;
	int temp_w = a.Mat_w;
	a.Mat_w = b.Mat_w;
	b.Mat_w = temp_w;
	double** temp_d;
	temp_d= a.Mat_da;
	a.Mat_da = b.Mat_da;
	b.Mat_da = temp_d;
}


void Matrix::showMatrix()
{

	for (int i = 0; i < Mat_he; i++)
	{
		for (int j = 0; j < Mat_w; j++)
		{
			if (Mat_da[i][j] < 0)
				cout << "_" << " ";
			else
				cout << setprecision(3) << Mat_da[i][j] << " ";
		}
		cout << endl;
	}
}

